using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Office.Interop.Outlook;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using TXLooker.Common;
using TXLooker.Common.Core;

namespace TXLooker.WorkItemExtention.Core
{
    public enum WIObjectType
    {
        Server,
        Project,
        QueriesRoot,
        QueriesGroup,
        Query
    }

    public abstract class WIObject : SelfUpdater, IComparable
    {
        public WIObject Parent
        {
            get { return this.m_parent; }
            private set
            {
                this.m_parent = value;
            }
        }
        private WIObject m_parent = null;

        private WIObject()
        {
            WIObject.AddObject(this);
        }
        public WIObject(WIObject parent)
            : this()
        {
            this.Parent = parent;
        }

        public Guid ID
        {
            get { return this.m_id; }
        }
        private Guid m_id = Guid.NewGuid();

        /// <summary>
        /// 
        /// </summary>
        public abstract WIObjectType Type { get; }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual WorkItemStore GetStore()
        {
            return this.Parent == null ? null : this.Parent.GetStore();
        }
        /// <summary>
        /// 
        /// </summary>       
        public MAPIFolder Folder
        {
            get { return this.m_folder; }
            protected set
            {
                if (this.m_folder != null) WIObject.UnBindObjectWithFolder(this.m_folder);
                this.m_folder = value;
                if (this.m_folder != null) WIObject.BindObjectWithFolder(this.m_folder, this);
            }
        }
        private MAPIFolder m_folder = null;

        public abstract OlFolderView FolderView { get; set; }

        public IEnumerable<WIObject> ChildItems
        {
            get
            {
                lock (this.m_childs)
                {
                    foreach (WIObject item in m_childs.Values)
                        yield return item;
                }
            }
        }
        protected Dictionary<object, WIObject> Childs
        {
            get { return this.m_childs; }
        }
        private Dictionary<object, WIObject> m_childs = new Dictionary<object, WIObject>();


        /// <summary>
        /// 
        /// </summary>
        public virtual void Delete()
        {
            if (this.Folder != null)
                WIObject.UnBindObjectWithFolder(this.Folder);

            lock (this.Childs)
            {
                foreach (WIObject item in this.Childs.Values)
                {
                    try
                    {
                        item.Delete();
                    }
                    catch (System.Exception ex)
                    {
                        Debug.WriteLine(ex.ToString());
                    }
                }
            }

            WIObject.RemoveObject(this);

            if (this.Folder != null)
            {
                //we do not need delete Server & Project folders
                switch (this.Type)
                {
                    case WIObjectType.Server:
                    case WIObjectType.Project:
                        break;
                    default:
                        this.Folder.Delete();
                        break;
                }
            }
        }

        public abstract string FolderName { get; }


        #region event NewChildItems

        public event TFEventHandler<IEnumerable<WIObject>> NewChildItems
        {
            add { lock (this.m_syncNewItems) { this.m_OnNewItemsEH += value; } }
            remove { lock (this.m_syncNewItems) { this.m_OnNewItemsEH -= value; } }
        }
        private TFEventHandler<IEnumerable<WIObject>> m_OnNewItemsEH = null;

        private readonly object m_syncNewItems = new object();

        protected void RaiseNewChildItems(IEnumerable<WIObject> items)
        {
            CommonGlobal.RaiseEventHandler(this.m_OnNewItemsEH, new object[] { this, items }, m_syncNewItems);
        }

        #endregion event NewChildItems

        #region event PreRemoveChildItems

        public event TFEventHandler<IEnumerable<WIObject>> PreRemoveChildItems
        {
            add { lock (this.m_syncPreRemoveItems) { this.m_OnPreRemoveItemsEH += value; } }
            remove { lock (this.m_syncPreRemoveItems) { this.m_OnPreRemoveItemsEH -= value; } }
        }
        private TFEventHandler<IEnumerable<WIObject>> m_OnPreRemoveItemsEH = null;

        private readonly object m_syncPreRemoveItems = new object();

        protected void RaisePreRemoveChildItems(IEnumerable<WIObject> items)
        {
            CommonGlobal.RaiseEventHandler(this.m_OnPreRemoveItemsEH, new object[] { this, items }, m_syncPreRemoveItems);
        }

        #endregion event PreRemoveChildItems


        public static WIObject GetObject(Guid id)
        {
            WIObject item = null;

            lock (m_instancesMAP)
            {
                if (m_instancesMAP.ContainsKey(id))
                    item = m_instancesMAP[id];
            }

            return item;
        }
        private static void AddObject(WIObject item)
        {
            lock (m_instancesMAP)
            {
                if (!m_instancesMAP.ContainsKey(item.ID))
                    m_instancesMAP.Add(item.ID, item);
            }
        }
        private static void RemoveObject(WIObject item)
        {
            UnBindObjectWithFolder(item.Folder);

            lock (m_instancesMAP)
            {
                m_instancesMAP.Remove(item.ID);
            }
        }
        //key - WIObject.ID
        private static Dictionary<Guid, WIObject> m_instancesMAP = new Dictionary<Guid, WIObject>();

        public static WIObject GetObjectByFolder(MAPIFolder folder)
        {
            WIObject item = null;

            lock (m_foldersMAP)
            {
                if (m_foldersMAP.ContainsKey(folder.EntryID))
                    item = GetObject(m_foldersMAP[folder.EntryID]);
            }

            return item;
        }
        public static void BindObjectWithFolder(MAPIFolder folder, WIObject item)
        {
            if (folder == null) return;

            lock (m_foldersMAP)
            {
                if (m_foldersMAP.ContainsKey(folder.EntryID))
                {
                    m_foldersMAP[folder.EntryID] = item.ID;
                }
                else
                {
                    m_foldersMAP.Add(folder.EntryID, item.ID);
                }
            }
        }
        public static void UnBindObjectWithFolder(MAPIFolder folder)
        {
            if (folder == null) return;

            lock (m_foldersMAP)
            {
                m_foldersMAP.Remove(folder.EntryID);
            }
        }
        //key - folder entry ID
        private static Dictionary<string, Guid> m_foldersMAP = new Dictionary<string, Guid>();

        /// <summary>
        /// Initializes Outlook folder.
        /// If folder exists it uses existing one, otherwise creates new folder.
        /// </summary>
        /// <param name="parent">parent folder instance</param>
        protected virtual void InitFolder(MAPIFolder parent)
        {
            foreach (MAPIFolder mapiFolder in parent.Folders)
            {
                if (String.CompareOrdinal(mapiFolder.Name, FolderName) == 0)
                {
                    Folder = mapiFolder;
                }
            }

            if (Folder == null)
            {
                Folder = parent.Folders.Add(FolderName, Missing.Value);
            }
        }

        public int CompareTo(object obj)
        {
            if (obj is WIObject)
            {
                var otherObject = (WIObject)obj;
                return FolderName.CompareTo(otherObject.FolderName);
            }
            throw new ArgumentException("object is not a WIObject");

        }
    }
}
